package com.eslag.rpc.provider;

import com.eslag.rpc.enumeration.RpcError;
import com.eslag.rpc.exception.RpcException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 一个简单的注册中心实现，实现两个主要功能：服务注册和服务查找。
 *
 *  serviceMap：保存服务名称和服务实例的映射关系。
 *  registeredService：保存已经注册的服务名称。
 *  register(T service)方法：将一个服务注册到注册中心。首先，检查服务是否已经被注册过，如果已经注册过，就不再重复注册。然后，获取这个服务实现的所有接口，将接口的全名作为服务名，和服务实例一起保存到serviceMap中。
 *  getService(String serviceName)方法：根据服务名称从注册中心获取一个服务实例。如果找不到对应的服务实例，就抛出一个异常。
 */
public class ServiceProviderImpl implements ServiceProvider {
    private static final Logger logger = LoggerFactory.getLogger(ServiceProviderImpl.class);

    // 将服务名与提供服务的对象的关系保存在一个ConcurrentHashMap中
    private static final Map<String, Object> serviceMap = new ConcurrentHashMap<>();
    // 用来保存当前有哪些对象被注册了
    private static final Set<String> registeredService = ConcurrentHashMap.newKeySet();

    /**
     * 保存服务到本地服务注册表
     * @param service 服务的实现类对象
     * @param serviceName 服务类名
     */
    @Override
    public <T> void addServiceProvider(T service, String serviceName) {
        if(registeredService.contains(serviceName)) return;

        registeredService.add(serviceName);
        serviceMap.put(serviceName, service);
        logger.info("向接口：{} 注册服务：{}", service.getClass().getInterfaces(), serviceName);
        // 获取这个服务实现的所有接口（反射机制）
//        Class<?>[] interfaces = service.getClass().getInterfaces();
//        if (interfaces.length == 0) {
//            throw new RpcException(RpcError.SERVICE_NOT_IMPLEMENT_ANY_INTERFACE);
//        }
        // 例如某个对象 A 实现了接口 X 和 Y，那么将 A 注册进去后，会有两个服务名 X 和 Y 对应于 A 对象。
        // 这种处理方式也就说明了某个接口只能对一个对象提供服务。
//        for (Class<?> i : interfaces) {
//            serviceMap.put(i.getCanonicalName(), service);
//        }
//        logger.info("向接口: {} 注册服务: {}", interfaces, serviceName);
    }

    /**
     * 根据服务名获得服务对象实例
     * @param serviceName
     * @return
     */
    @Override
    public Object getServiceProvider(String serviceName) {
        Object service = serviceMap.get(serviceName);
        if (service == null) {
            throw new RpcException(RpcError.SERVICE_NOT_FOUND);
        }
        return service;
    }
}
